package authcomment

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	"blogcenter/internal/config"
	"blogcenter/internal/middleware"
	"blogcenter/internal/model"
	"blogcenter/internal/svc"
	"blogcenter/internal/types"
	"blogcenter/internal/utils"

	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/rest/httpc"
)

type CommentBlogLogic struct {
	logx.Logger
	ctx         context.Context
	svcCtx      *svc.ServiceContext
	GorseClient *utils.GorseClient
}

// 全局批量处理器
var (
	commentBatcher *model.CommentBatcher
	once           sync.Once
)

func NewCommentBlogLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CommentBlogLogic {
	// 初始化批量处理器(单例)
	once.Do(func() {
		commentBatcher = model.NewCommentBatcher(svcCtx.CommentModel)
		go commentBatcher.Start()
	})

	return &CommentBlogLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *CommentBlogLogic) CommentBlog(req *types.CommentRequest) (*types.BaseResponse, error) {
	// 1. 基础验证
	if err := validateCommentRequest(req); err != nil {
		return nil, err
	}
	userId, ok := l.ctx.Value(middleware.UserIdKey).(int)
	if !ok || userId == 0 {
		return nil, errors.New("未授权访问")
	}

	// 2. 敏感词检查
	if contains, words := l.checkSensitiveWords(req.Content); contains {
		return &types.BaseResponse{
			Code:    400,
			Message: "评论包含敏感词: " + words,
		}, nil
	}

	// 3. 生成评论ID (提前生成以便追踪)
	commentID := generateCommentID(req.ObjectID, userId)

	// 4. 发送到Kafka进行异步处理
	if err := l.sendToKafka(l.ctx, req, commentID, userId); err != nil {
		logx.Errorf("发送评论到Kafka失败: %v", err)
		return &types.BaseResponse{
			Code:    500,
			Message: "系统繁忙，请稍后重试" + err.Error(),
		}, nil
		// return nil, err
	}

	// 异步同步到 Gorse
	go func() {
		ctx := context.Background()
		err := l.svcCtx.GorseClient.FeedBackGorse(ctx, int(userId), int(req.ObjectID), "comment", time.Now())
		if err != nil {
			logx.Errorf("同步评论到Gorse失败: %v", err)
		}
	}()

	// 加积分
	_, err := l.addCredit(int(userId))
	if err != nil {
		logx.Errorf("添加积分失败: %v", err)
	}

	return &types.BaseResponse{
		Code:    200,
		Message: "评论提交成功，正在处理",
	}, nil
}

// 验证评论请求
func validateCommentRequest(req *types.CommentRequest) error {
	if req.ObjectID == 0 {
		return errors.New("object_id不能为空")
	}
	if len(req.Content) == 0 {
		return errors.New("评论内容不能为空")
	}
	if len(req.Content) > 1000 {
		return errors.New("评论内容不能超过1000个字符")
	}
	return nil
}

// 检查敏感词
func (l *CommentBlogLogic) checkSensitiveWords(content string) (bool, string) {
	sensitiveWords := []string{"fuck"} // 示例敏感词列表，实际根据业务需求进行修改
	for _, word := range sensitiveWords {
		if strings.Contains(content, word) {
			return true, word
		}
	}
	return false, ""
}

// 发送到Kafka
func (l *CommentBlogLogic) sendToKafka(ctx context.Context, req *types.CommentRequest, commentID uint, userID int) error {
	message := map[string]interface{}{
		"commentId":  commentID,
		"objectId":   req.ObjectID,
		"objectType": req.ObjectType,
		"userId":     userID,
		"content":    req.Content,
		"parentId":   req.ParentID,
		"rootId":     req.RootID,
		"timestamp":  time.Now().Unix(),
	}

	jsonData, err := json.Marshal(message)
	if err != nil {
		return err
	}

	// 使用 object_id 作为 key 确保相同对象的评论进入同一分区
	key := fmt.Sprintf("%d", req.ObjectID)

	return l.svcCtx.KafkaPusher.PushWithKey(ctx, key, string(jsonData))
}

// 生成评论ID
func generateCommentID(objectID uint, userID int) uint {
	return uint(time.Now().UnixNano()/1000) + objectID%1000 + uint(userID%1000)
}

func (l *CommentBlogLogic) getUserInfo(userID int) (*types.UserBrief, error) {
	// 1. 定义响应结构体
	type UserInfoResponse struct {
		Code    int    `json:"code"`
		Message string `json:"message"`
		Data    struct {
			ID     int    `json:"id"`
			Name   string `json:"name"`
			Avatar string `json:"avatar"`
		} `json:"data"`
	}
	// 确保context不为nil
	if l.ctx == nil {
		l.ctx = context.Background()
		logx.WithContext(l.ctx).Errorf("context为nil，使用background context")
	}
	cfg := config.GetBaseConfig()
	consulURL := fmt.Sprintf("http://%s:%s", cfg.Consul.Host, "/v1/health/service/user-api?passing")
	// 2. 从Consul获取user-api服务地址
	// consulURL := "http://127.0.0.1:8500/v1/health/service/user-api?passing"
	consulResp, err := httpc.Do(l.ctx, "GET", consulURL, nil)
	if err != nil {
		logx.WithContext(l.ctx).Errorf("consul请求失败, error: %v", err)
		return nil, fmt.Errorf("获取服务地址失败: %v", err)
	}
	defer consulResp.Body.Close()

	if consulResp.StatusCode != http.StatusOK {
		body, _ := ioutil.ReadAll(consulResp.Body)
		logx.WithContext(l.ctx).Errorf("consul返回非200状态码, status: %d, body: %s",
			consulResp.StatusCode, string(body))
		return nil, fmt.Errorf("consul返回错误状态码: %d", consulResp.StatusCode)
	}

	var services []struct {
		Service struct {
			Address string `json:"Address"`
			Port    int    `json:"Port"`
		} `json:"Service"`
	}

	consulBody, err := ioutil.ReadAll(consulResp.Body)
	if err != nil {
		logx.WithContext(l.ctx).Errorf("读取consul响应体失败, error: %v", err)
		return nil, fmt.Errorf("读取服务地址失败: %v", err)
	}

	if err := json.Unmarshal(consulBody, &services); err != nil {
		logx.WithContext(l.ctx).Errorf("解析consul响应失败, error: %v, body: %s",
			err, string(consulBody))
		return nil, fmt.Errorf("解析服务地址失败: %v", err)
	}

	if len(services) == 0 {
		logx.WithContext(l.ctx).Errorf("无可用user-api服务实例")
		return nil, fmt.Errorf("无可用服务实例")
	}

	// 随机选择一个实例
	rand.Seed(time.Now().UnixNano())
	instance := services[rand.Intn(len(services))]
	baseURL := fmt.Sprintf("http://%s:%d", instance.Service.Address, instance.Service.Port)

	// 3. 构建请求URL
	// url := fmt.Sprintf("%s/api/user/info/%d", baseURL, userID)

	// 4. 发送HTTP请求到用户服务
	// resp, err := httpc.Do(l.ctx, "GET", "", url)
	resp, err := httpc.Do(l.ctx, "GET", baseURL+"/api/userbase/info/"+strconv.Itoa(userID), nil)
	if err != nil {
		logx.WithContext(l.ctx).Errorf("获取用户信息请求失败, userID: %d, error: %v", userID, err)
		return nil, fmt.Errorf("获取用户信息请求失败: %v", err)
	}
	defer resp.Body.Close()

	// 5. 检查HTTP状态码
	if resp.StatusCode != http.StatusOK {
		body, _ := ioutil.ReadAll(resp.Body)
		logx.WithContext(l.ctx).Errorf("用户服务返回非200状态码, userID: %d, status: %d, body: %s",
			userID, resp.StatusCode, string(body))
		return nil, fmt.Errorf("用户服务返回错误状态码: %d", resp.StatusCode)
	}

	// 6. 读取响应体
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		logx.WithContext(l.ctx).Errorf("读取响应体失败, userID: %d, error: %v", userID, err)
		return nil, fmt.Errorf("读取响应体失败: %v", err)
	}

	// 7. 解析JSON
	var result UserInfoResponse
	if err := json.Unmarshal(body, &result); err != nil {
		logx.WithContext(l.ctx).Errorf("解析用户信息响应失败, userID: %d, error: %v, body: %s",
			userID, err, string(body))
		return nil, fmt.Errorf("解析用户信息响应失败: %v", err)
	}

	// 8. 检查业务状态码
	if result.Code != 200 {
		logx.WithContext(l.ctx).Errorf("用户服务返回业务错误, userID: %d, code: %d, message: %s",
			userID, result.Code, result.Message)
		return nil, fmt.Errorf("用户服务错误: %s", result.Message)
	}

	// 9. 返回用户信息
	return &types.UserBrief{
		ID:     result.Data.ID,
		Name:   result.Data.Name,
		Avatar: result.Data.Avatar,
	}, nil
}


func (l *CommentBlogLogic) addCredit(userID int) (*types.BaseResponse, error) {
	// 1. 定义响应结构体
	type addRequest struct {
		UserID    int    `json:"userId"`
		Credit    int    `json:"credit"`
		Operation string `json:"operation"`
	}
	add := addRequest{
		UserID:    userID,
		Credit:    1,
		Operation: "add",
	}
	// 确保context不为nil
	if l.ctx == nil {
		l.ctx = context.Background()
		logx.WithContext(l.ctx).Errorf("context为nil，使用background context")
	}
	cfg := config.GetBaseConfig()
	consulURL := fmt.Sprintf("http://%s:%s", cfg.Consul.Host, "/v1/health/service/user-api?passing")
	// 2. 从Consul获取user-api服务地址
	// consulURL := "http://127.0.0.1:8500/v1/health/service/user-api?passing"
	consulResp, err := httpc.Do(l.ctx, "GET", consulURL, nil)
	if err != nil {
		logx.WithContext(l.ctx).Errorf("consul请求失败, error: %v", err)
		return nil, fmt.Errorf("获取服务地址失败: %v", err)
	}
	defer consulResp.Body.Close()

	if consulResp.StatusCode != http.StatusOK {
		body, _ := ioutil.ReadAll(consulResp.Body)
		logx.WithContext(l.ctx).Errorf("consul返回非200状态码, status: %d, body: %s",
			consulResp.StatusCode, string(body))
		return nil, fmt.Errorf("consul返回错误状态码: %d", consulResp.StatusCode)
	}

	var services []struct {
		Service struct {
			Address string `json:"Address"`
			Port    int    `json:"Port"`
		} `json:"Service"`
	}

	consulBody, err := ioutil.ReadAll(consulResp.Body)
	if err != nil {
		logx.WithContext(l.ctx).Errorf("读取consul响应体失败, error: %v", err)
		return nil, fmt.Errorf("读取服务地址失败: %v", err)
	}

	if err := json.Unmarshal(consulBody, &services); err != nil {
		logx.WithContext(l.ctx).Errorf("解析consul响应失败, error: %v, body: %s",
			err, string(consulBody))
		return nil, fmt.Errorf("解析服务地址失败: %v", err)
	}

	if len(services) == 0 {
		logx.WithContext(l.ctx).Errorf("无可用user-api服务实例")
		return nil, fmt.Errorf("无可用服务实例")
	}

	// 随机选择一个实例
	rand.Seed(time.Now().UnixNano())
	instance := services[rand.Intn(len(services))]
	baseURL := fmt.Sprintf("http://%s:%d", instance.Service.Address, instance.Service.Port)

	// 3. 构建请求URL
	// url := fmt.Sprintf("%s/api/user/info/%d", baseURL, userID)

	// 4. 将结构体序列化为 JSON
	jsonData, err := json.Marshal(add)
	if err != nil {
		logx.WithContext(l.ctx).Errorf("JSON序列化失败, error: %v", err)
		return nil, fmt.Errorf("请求参数错误: %v", err)
	}

	// 创建标准 HTTP 请求
	req, err := http.NewRequest(
		"POST",
		baseURL+"/api/baseuser/add",
		bytes.NewBuffer(jsonData), // 使用原始 jsonData
	)
	if err != nil {
		return nil, err
	}

	// 设置 Header
	req.Header.Set("Content-Type", "application/json")

	// 发送请求
	client := &http.Client{}
	resp, _ := client.Do(req.WithContext(l.ctx))

	// 5. 检查HTTP状态码
	if resp.StatusCode != http.StatusOK {
		body, _ := ioutil.ReadAll(resp.Body)
		logx.WithContext(l.ctx).Errorf("用户服务返回非200状态码, userID: %d, status: %d, body: %s",
			userID, resp.StatusCode, string(body))
		return nil, fmt.Errorf("用户服务返回错误状态码: %d", resp.StatusCode)
	}

	// 6. 读取响应体
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		logx.WithContext(l.ctx).Errorf("读取响应体失败, userID: %d, error: %v", userID, err)
		return nil, fmt.Errorf("读取响应体失败: %v", err)
	}

	// 7. 解析JSON
	var result types.BaseResponse
	if err := json.Unmarshal(body, &result); err != nil {
		logx.WithContext(l.ctx).Errorf("解析用户信息响应失败, userID: %d, error: %v, body: %s",
			userID, err, string(body))
		return nil, fmt.Errorf("解析用户信息响应失败: %v", err)
	}

	// 8. 检查业务状态码
	if result.Code != 200 {
		logx.WithContext(l.ctx).Errorf("用户服务返回业务错误, userID: %d, code: %d, message: %s",
			userID, result.Code, result.Message)
		return nil, fmt.Errorf("用户服务错误: %s", result.Message)
	}

	// 9. 返回信息
	return &types.BaseResponse{
		Code:    result.Code,
		Message: result.Message,
	}, nil
}